package config

import (
	"fmt"
	"io/ioutil"
	"strings"

	log "github.com/sirupsen/logrus"

	"os"
)

type AppConfig interface {
	MetricsPath() string
	ListenPort() string
	LogLevel() string
	ApplicationName() string
}

type BaseConfig struct {
	metricsPath string
	listenPort  string
	logLevel    string
	appName     string
}

// Config struct holds all of the runtime configuration for the application
type Config struct {
	*BaseConfig
	APIURL        string
	Repositories  string
	Organisations string
	Users         string
	APITokenEnv   string
	APITokenFile  string
	APIToken      string
	TargetURLs    []string
}

func (c BaseConfig) MetricsPath() string {
	return c.metricsPath
}

func (c BaseConfig) ListenPort() string {
	return c.listenPort
}

func (c BaseConfig) LogLevel() string {
	return c.logLevel
}

func (c BaseConfig) ApplicationName() string {
	return c.appName
}

func InitBase() BaseConfig {

	appConfig := BaseConfig{
		metricsPath: GetEnv("METRICS_PATH", "/metrics"),
		listenPort:  GetEnv("LISTEN_PORT", "9190"),
		logLevel:    GetEnv("LOG_LEVEL", "debug"),
		appName:     GetEnv("APP_NAME", "app"),
	}
	return appConfig
}

// Init populates the Config struct based on environmental runtime configuration
func Init() Config {

	ac := InitBase()
	url := GetEnv("API_URL", "https://gitee.com/api/v5")
	repos := os.Getenv("REPOS")
	orgs := os.Getenv("ORGS")
	users := os.Getenv("USERS")
	tokenEnv := os.Getenv("GITEE_TOKEN")
	tokenFile := os.Getenv("GITEE_TOKEN_FILE")
	token, err := getAuth(tokenEnv, tokenFile)
	scraped, err := getScrapeURLs(url, repos, orgs, users)

	if err != nil {
		log.Errorf("Error initialising Configuration, Error: %v", err)
	}

	appConfig := Config{
		&ac,
		url,
		repos,
		orgs,
		users,
		tokenEnv,
		tokenFile,
		token,
		scraped,
	}

	return appConfig
}

// Init populates the Config struct based on environmental runtime configuration
// All URL's are added to the TargetURL's string array
func getScrapeURLs(apiURL, repos, orgs, users string) ([]string, error) {

	urls := []string{}

	opts := "?&per_page=20" // Used to set the Gitee API to return 20 results per page

	// User input validation, check that either repositories or organisations have been passed in
	if len(repos) == 0 && len(orgs) == 0 && len(users) == 0 {
		return urls, fmt.Errorf("No targets specified")
	}

	// Append repositories to the array
	if repos != "" {
		rs := strings.Split(repos, ", ")
		for _, x := range rs {
			y := fmt.Sprintf("%s/repos/%s%s", apiURL, x, opts)
			urls = append(urls, y)
		}
	}

	// Append gitee orginisations to the array
	if orgs != "" {
		o := strings.Split(orgs, ", ")
		for _, x := range o {
			y := fmt.Sprintf("%s/orgs/%s/repos%s", apiURL, x, opts)
			urls = append(urls, y)
		}
	}

	// Append gitee user to the array
	if users != "" {
		us := strings.Split(users, ", ")
		for _, x := range us {
			y := fmt.Sprintf("%s/users/%s/repos%s", apiURL, x, opts)
			urls = append(urls, y)
		}
	}

	return urls, nil
}

// getAuth returns oauth2 token as string for usage in http.request
func getAuth(token string, tokenFile string) (string, error) {

	if token != "" {
		return token, nil
	} else if tokenFile != "" {
		b, err := ioutil.ReadFile(tokenFile)
		if err != nil {
			return "", err
		}
		return strings.TrimSpace(string(b)), err

	}

	return "", nil
}

// GetEnv - Allows us to supply a fallback option if nothing specified
func GetEnv(key, fallback string) string {
	value := os.Getenv(key)
	if len(value) == 0 {
		return fallback
	}
	return value
}
